﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Xml;

namespace FormatX
{
    #region Class SummaryXml
    /// <summary>
    ///  SummaryXml
    /// </summary>
    internal class SummaryXml : IDisposable
    {
        #region Identity
        private string _projectPath = string.Empty;
        private string _projectName = string.Empty;
        private string _fileName = string.Empty;
        private string _projectFullName = string.Empty;
        private string _csXml = string.Empty;
        private string _csDll = string.Empty;
        private List<string> _names = new List<string>();
        private string _refDll = string.Empty;
        /// <summary>
        /// 格式化前后可以订阅的事件
        /// </summary>
        /// <param name="state">指示当前的情况分别有：start,end</param>
        /// <param name="e"></param>
        internal delegate void OnFormat(string state, EventArgs e);
        internal event OnFormat OnStartFormat, OnEndFormat;
        #endregion

        #region Function SummaryXml
        /// <summary>
        ///  SummaryXml
        /// </summary>
        /// <param name='projectPath'>projectPath</param>
        /// <param name='fileName'>fileName</param>
        /// <param name='projectName'>projectName</param>
        /// <param name='projectFullName'>projectFullName</param>
        public SummaryXml(string projectPath, string fileName, string projectName, string projectFullName)
        {
            _projectPath = projectPath;
            _fileName = fileName;
            _projectName = projectName;
            _projectFullName = projectFullName;
            _csXml = string.Format("{0}.xml", _fileName);
            _csDll = string.Format("{0}.dll", _fileName);
        }
        #endregion

        #region Function StartXml
        /// <summary>
        ///  StartXml
        /// </summary>
        internal List<string> StartXml()
        {
            //  开始格式化前执行委托
            if (OnStartFormat != null)
                OnStartFormat("start", null);

            if (_csXml.Length < 5)
            {
                throw new FileNotFoundException("项目不存在。");
            }

            // 加载引用项
            CheckRef();
            // 检查编译器版本
            string version = GetVersion();

            /*
             * 使用MSBuild进行编译时，更改为以下代码
             *  string args = string.Format("{0} {1} {2}", _projectFullName, "/t:rebuild", "/nologo");
             *  System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(@"C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe", args);
             * 
             */            
            string args = string.Format("/out:{0} /target:library {1} /warn:0 /nologo  /doc:{2} /r:{3} /noconfig /nostdlib", _csDll, _fileName, _csXml, _refDll);
            string cscexe = string.Format(@"C:\WINDOWS\Microsoft.NET\Framework\{0}\csc.exe", version);
            System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(cscexe, args);
            psi.UseShellExecute = false;
            psi.RedirectStandardOutput = true;
            psi.CreateNoWindow = true;
            using (System.Diagnostics.Process process = System.Diagnostics.Process.Start(psi))
            {

                // 捕获输出
                string ret = process.StandardOutput.ReadToEnd();
                if (!string.IsNullOrEmpty(ret))
                {
                    throw new Exception("创建xml文件时出错，请检查代码错误后尝试。\n" + ret);
                }
                process.WaitForExit();
                process.Close();
                process.Dispose();
            }
            DeSerialize(_csXml);

            Dispose();
            //  格式化完成后执行委托
            if (OnEndFormat != null)
                OnEndFormat("end", null);
            return _names;
        }
        #endregion

        #region Function CheckXml
        /// <summary>
        ///  CheckXml
        /// </summary>
        /// <param name='xmlFile'>xmlFile</param>
        private bool CheckXml(string xmlFile)
        {
            return File.Exists(xmlFile);
        }
        #endregion

        #region Function DeSerialize
        /// <summary>
        ///  DeSerialize
        /// </summary>
        /// <param name='xmlFile'>xmlFile</param>
        private void DeSerialize(string xmlFile)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(xmlFile);
            if (doc.HasChildNodes)
            {
                XmlNode root = doc.ChildNodes[1];
                XmlNodeList members = root.ChildNodes;
                for (int i = 0; i < members.Count; i++)
                {
                    if (members[i].Name.ToLower() == "members")
                    {
                        for (int j = 0; j < members[i].ChildNodes.Count; j++)
                        {
                            XmlNode member = members[i].ChildNodes[j];
                            XmlNode summary = member.ChildNodes[0];
                            if (summary != null)
                            {
                                ExtractName(member.Attributes["name"].Value);
                            }
                        }
                    }
                }
            }
        }
        #endregion

        #region Function ExtractName
        /// <summary>
        ///  ExtractName
        /// </summary>
        /// <param name='nodeName'>nodeName</param>
        private void ExtractName(string nodeName)
        {
            if (string.IsNullOrEmpty(nodeName)) return;
            nodeName = nodeName.Substring(nodeName.IndexOf(':') + 1);
            //  处理构造方法
            if (nodeName.Contains("#ctor"))
            {
                // 取得方法名
                string t1 = nodeName.Substring(0, nodeName.LastIndexOf('#') - 1);
                // 取得方法名
                string t2 = t1.Substring(t1.LastIndexOf('.') + 1);
                string t3 = nodeName.Substring(nodeName.LastIndexOf('#') + 5);
                nodeName = string.Format("{0}.{1}{2}", t1, t2, t3);
            }
            _names.Add(nodeName);
        }
        #endregion

        #region Function CheckRef
        /// <summary>
        /// 加载引用程序集，以便进行编译
        /// </summary>
        public void CheckRef()
        {
            string prodir = _projectFullName;
            prodir = prodir.Substring(0, prodir.LastIndexOf('\\')) + "\\bin\\";
            List<string> files = new List<string>();
            files.AddRange(System.IO.Directory.GetFiles(prodir, "*.dll", System.IO.SearchOption.AllDirectories));
            files.AddRange(System.IO.Directory.GetFiles(prodir, "*.exe", System.IO.SearchOption.AllDirectories));

            if (files != null && files.Count > 0)
            {
                // 加载此程序所引用的程序集列表
                string assemblyname = string.Empty;
                for (int i = 0; i < files.Count; i++)
                {
                    if (files[i].Contains(_projectName + ".dll") || files[i].Contains(_projectName + ".exe"))
                    {
                        assemblyname = files[i];
                        break;
                    }
                }

                if (!string.IsNullOrEmpty(assemblyname))
                {
                    //  以字节流的方式加载当前程序集
                    //  如果直接Assembly.Load文件，会导致程序集被加载到当前AppDomain，无法释放。
                    using (FileStream fs = new FileStream(assemblyname, FileMode.Open, FileAccess.Read))
                    {
                        int len = (int)fs.Length;
                        byte[] assemblyByte = new byte[len];
                        fs.Read(assemblyByte, 0, len);
                        fs.Flush();
                        fs.Close();
                        fs.Dispose();

                        System.Reflection.AssemblyName[] abn = System.Reflection.Assembly.Load(assemblyByte).GetReferencedAssemblies();
                        for (int i = 0; i < abn.Length; i++)
                        {
                            try
                            {
                                System.Reflection.Assembly ay = System.Reflection.Assembly.Load(abn[i].FullName);
                                files.Add(ay.Location);
                            }
                            catch { }
                        }
                    }
                }
                for (int i = 0; i < files.Count; i++)
                {
                    int index = files[i].LastIndexOf('\\');
                    if (index > 0 && _refDll.Contains(files[i].Substring(index)))
                    {
                        continue;
                    }
                    else if (i + 1 == files.Count)
                        _refDll += files[i];
                    else
                        _refDll += string.Format("{0},", files[i]);
                }
            }
        }
        #endregion

        #region Function GetVersion
        private string GetVersion()
        {
            string version = "v3.5";
            try
            {
                XmlDocument configs = new XmlDocument();
                configs.Load(this.GetType().Assembly.Location + ".config");
                XmlNodeList nodelist = configs["configuration"]["appSettings"].ChildNodes;
                if (nodelist != null)
                {
                    for (int i = 0; i < nodelist.Count; i++)
                    {
                        if (nodelist[i].Name == "add" && nodelist[i].Attributes["key"].Value == "version")
                        {
                            switch (nodelist[i].Attributes["value"].Value)
                            {
                                case "2008":
                                    version = "v3.5";
                                    break;
                                case "2010":
                                    version = "v4.0.30319";
                                    break;
                                default:
                                    version = "v3.5";
                                    break;
                            }
                            break;
                        }
                    }
                }
            }
            catch { }
            return version ?? "v3.5";
        }
        #endregion

        #region Function Dispose
        /// <summary>
        ///  Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                if (CheckXml(_csXml))
                {
                    File.Delete(_csXml);
                    File.Delete(_csXml.Replace(".xml", ".dll"));
                }
            }
            catch { }
        }
        #endregion
    }
    #endregion
}
